package net.siufung.core.utils;

import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.siufung.core.exception.BizServiceException;
import net.siufung.core.result.ResultFail;
import org.hibernate.validator.HibernateValidator;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.io.Serializable;
import java.util.List;
import java.util.Set;

/**
 * @author 陈建峰
 * @since 2022/4/24 2:31 下午
 */
@Slf4j
public class ValidationUtil {

    /**
     * 开启快速结束模式
     */
    private static final Validator VALIDATOR = Validation.byProvider(HibernateValidator.class)
            .configure()
            .failFast(false)
            .buildValidatorFactory()
            .getValidator();

    /**
     * 校验bean的某一个属性
     * @param obj          bean
     * @param propertyName 属性名称
     * @return ValidResult
     */
    public static <T> ValidResult validateProperty(T obj, String propertyName) {
        ValidResult result = new ValidResult();
        Set<ConstraintViolation<T>> violationSet = VALIDATOR.validateProperty(obj, propertyName);
        boolean hasError = violationSet != null && violationSet.size() > 0;
        result.setHasErrors(hasError);
        if (hasError) {
            for (ConstraintViolation<T> violation : violationSet) {
                result.addError(propertyName, violation.getMessage());
            }
        }
        return result;
    }

    /**
     * 校验Bean
     * @param t bean
     * @param groups 校验组
     * @return ValidResult
     */
    public static <T> ValidResult validateBean(T t, Class<?>...groups) {
        ValidResult result = new ValidResult();
        Set<ConstraintViolation<T>> violationSet = VALIDATOR.validate(t,groups);
        boolean hasError = violationSet != null && violationSet.size() > 0;
        result.setHasErrors(hasError);
        if (hasError) {
            for (ConstraintViolation<T> violation : violationSet) {
                result.addError(violation.getPropertyPath().toString(), violation.getMessage());
            }
        }
        return result;
    }

    /**
     * 校验Bean
     * @param t bean
     * @param groups 校验组
     * @return Boolean
     */
    public static <T> Boolean validated(T t, Class<?>...groups) {
        ValidationUtil.ValidResult validResult = validateBean(t, groups);
        if(validResult.hasErrors()){
            log.error(validResult.getErrors());
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 校验结果类
     */
    @Data
    public static class ValidResult {
        /**
         * 是否有错误
         */
        private boolean hasErrors;
        /**
         * 错误信息
         */
        private List<ErrorMessage> errors;

        /**
         * 构造
         */
        public ValidResult() {
            this.errors = Lists.newArrayList();
        }

        /**
         * 添加属性错误
         * @param propertyName 属性名称
         * @param message 错误消息
         */
        public void addError(String propertyName, String message) {
            this.setHasErrors(true);
            this.errors.add(new ErrorMessage(propertyName, message));
        }

        /**
         * 添加属性错误
         * @param errorMessage 错误消息
         */
        public void addError(ErrorMessage errorMessage) {
            this.setHasErrors(true);
            this.errors.add(errorMessage);
        }

        /**
         * 存在错误
         * @return boolean
         */
        public boolean hasErrors() {
            return hasErrors;
        }

        /**
         * 获取所有验证信息
         * @return 字符串形式
         */
        public String getErrors(){
            StringBuilder sb = new StringBuilder();
            for (ErrorMessage error : errors) {
                sb.append(error.getMessage()).append(" ");
            }
            return sb.toString();
        }
    }

    @Data
    @AllArgsConstructor
    public static class ErrorMessage {
        /**
         * 属性名称
         */
        private String propertyName;
        /**
         * 错误消息
         */
        private String message;
    }

    /**
     * 验证是否合法
     */
    public interface Validatable extends Serializable {

        /**
         * 校验Dto传参值返回成功与否
         * @return true/false
         */
        default Boolean validated() {
            ValidResult validResult = validResult();
            if(validResult.hasErrors()){
                throw new BizServiceException(ResultFail.PARAM_VALID_ERROR+":【"+validResult.getErrors()+"】");
            }
            return Boolean.TRUE;
        }

        /**
         * 校验Dto传参值返回 ValidResult
         * @return ValidResult
         */
        default ValidResult validResult() {
            return ValidationUtil.validateBean(this);
        }

    }
}
